network = (function () {

    function _base64ToArrayBuffer(base64) {
        var binary_string = dcodeIO.ByteBuffer.atob(base64);
        var len = binary_string.length;
        var bytes = new Uint8Array(len);
        for (var i = 0; i < len; i++) {
            bytes[i] = binary_string.charCodeAt(i);
        }
        return bytes.buffer;
    }

    var client = null;

    var isConnected = false;
    var listenerMap = {};

    var msgQueue = [];
    var timer = null;

    var disconnectCb = null;

    var isStop = false;
    var excepts = [];

    function clearMsgs() {
        msgQueue = [];
        isStop = false;
    }

    function stop(e) {
        excepts = e || [];
        isStop = true;
    }

    function start() {
        excepts = [];
        isStop = false;
    }

    function trace(count) {
        var caller = arguments.callee.caller;
        var i = 0;
        count = count || 10;
        console.log("***----------------------------------------  ** " + (i + 1));
        while (caller && i < count) {
            console.log(caller.toString());
            caller = caller.caller;
            i++;
            console.log("***---------------------------------------- ** " + (i + 1));
        }
    }

    function update() {
        //noinspection LoopStatementThatDoesntLoopJS
        while (msgQueue.length > 0) {
            var obj = msgQueue[0];
            if (isStop && excepts.indexOf(obj.code) < 0)
                return;
            if (obj['base64data']) {
                if (obj['base64data']) {
                    var NiuNiuChangeProxyID = [P.GS_Login, P.GS_GameStart, P.GS_RoomResult, P.GS_Vote];

                    // console.log("gameData.wanfatype==" + gameData.wanfatype);
                    if (NiuNiuChangeProxyID.indexOf(obj.code) >= 0)
                        obj.code += 500;

                    var messageType = ProtoTypes[obj.code.toString()];   //'GS_UserJoin_MSG';
                    // console.log("messageType==" + messageType);
                    if (messageType == undefined) {
                        initProto();
                        // mProto.initProtoTypes();
                        messageType = ProtoTypes[obj.code.toString()];
                        if (messageType == undefined) {
                            msgQueue.shift();
                            return;
                        }
                    }

                    var builder = mProto.builder.build("paohuzi." + messageType);
                    if (!builder) {
                        msgQueue.shift();
                        continue;
                    }
                    var msgArr = _base64ToArrayBuffer(obj['base64data']);

                    //console.log("paohuzi==msgArr==="+msgArr);
                    var msgObj = builder.decode(msgArr);
                    //console.log("paohuzi==msgObj==="+msgObj);

                    msgQueue.shift();

                    if (NiuNiuChangeProxyID.indexOf(obj.code - 500) >= 0)
                        obj.code -= 500;

                    console.log(timestamp2time(getCurTimestamp()) + "==" + (new Date().getTime()) + "=code=" + obj.code + " recv: " + JSON.stringify(msgObj));


                    do {
                        if (cc.sys.isNative) {
                            try {
                                listenerMap[obj.code](msgObj);
                            } catch (e) {
                                if (typeof e === 'string') {
                                    alert1(e + ", code=" + obj.code);
                                }
                                else {
                                    alert1(e.message + ", code=" + obj.code);
                                }
                            }
                            break;
                        }
                        if(listenerMap[obj.code])  listenerMap[obj.code](msgObj);
                    } while (false);
                }
                break;
            } else {
                if (listenerMap[obj.code]) {
                    msgQueue.shift();
                    if (cc.sys.isNative)
                        console.log(timestamp2time(getCurTimestamp()) + " recv: " + JSON.stringify(obj));
                    else
                        console.log(
                            "%c[" + timestamp2time(getCurTimestamp()) + "] %crecv%c " + JSON.stringify(obj),
                            "background-color:#dfdfdf; color:blue",
                            "background-color:grey; color:blue;",
                            "color:black"
                        );

                    obj.errorCode = obj.errorCode || 0;
                    obj.errorMsg = obj.errorMsg || '';

                    do {
                        if (obj.errorCode && obj.errorMsg) {
                            var errorMsg = obj.errorMsg;
                            alert1(errorMsg, null, null, true, true, true);
                            break;
                        }

                        if (cc.sys.isNative) {
                            try {
                                listenerMap[obj.code](obj.data, obj.errorCode);
                            } catch (e) {
                                if (typeof e === 'string') {
                                    alert1(e + ", code=" + obj.code);
                                }
                                else {
                                    var arr = e.stack.match(/(src\/\w+.js:[0-9]*)|(<.\w+@)/g);
                                    if (arr) {
                                        alert1(arr[0] + "\n" + arr[1] + "\nmessage=" + e.message + "\ncode=" + obj.code);
                                    } else {
                                        alert1(e.message + ", code=" + obj.code);
                                    }
                                }
                            }
                            break;
                        }

                        listenerMap[obj.code](obj.data, obj.errorCode);
                    } while (false);

                }
                else
                    cc.log("recv eee: " + obj.code);

                break;
            }
        }
    }

    function getNextMsgCode() {
        if (msgQueue.length < 1)
            return 0;
        if (msgQueue[0] && msgQueue[0].code)
            return msgQueue[0].code;
        return 0;
    }

    function onConnectSuccess() {
        client.subscribe("n");
        cc.log("onConnect");
        isConnected = true;
        timer = setInterval(update, 50);
    }

    function onConnectFailure() {
        isConnected = false;
    }

    function onConnectionLost(responseObject) {
        clearInterval(timer);
        clearMsgs();
        if (responseObject.errorCode !== 0) {
            cc.log("onConnectionLost:" + responseObject.errorMessage);
        }
        isConnected = false;
        if (disconnect)
            disconnectCb();
    }

    function onMessageArrived(message) {
        var bytes = message.payloadBytes;
        var a = parseInt(bytes[0]), b = parseInt(bytes[1]);
        var len = a * 256 + b;

        var arr = [];
        for (var j = 2; j < 2 + len; j++)
            arr.push(bytes[j]);

        // todo rc4

        try {
            arr = bzip2.simple(bzip2.array(arr));
        } catch (e) {

        }

        arr = UTF8ArrayToUTF16Array(arr);

        var str = String.fromCharCode.apply(null, arr);
        str = _.trim(str);
        str = str.replace(/'/g, "\"");
        // str = decodeURIComponent(str);
        str = str.replace(/\n/g, "\\n");

        try {
            var obj = JSON.parse(str);
            decodeObj(obj);
            msgQueue.push(obj);
        } catch (e) {
            cc.log(str);
            cc.log(e);
        }

        if (!obj) {
            var arr = [];
            for (var i = a; i <= b; i++)
                arr.push(bytes[i]);
            var str = String.fromCharCode.apply(null, arr);
            alert1('can not parse: ' + str);
        }
    }

    function decodeObj(obj) {
        if (_.isObject(obj)) {
            for (var o in obj) {
                if (obj.hasOwnProperty(o)) {
                    if (_.isString(obj[o])) {
                        obj[o] = decodeURIComponent(obj[o]);
                    } else if (_.isObject(obj[o]) || _.isArray(obj[o])) {
                        decodeObj(obj[o]);
                    }
                }
            }
        } else if (_.isArray(obj)) {
            for (var i = 0; i < obj.length; i++) {
                if (_.isString(obj[i])) {
                    obj[i] = decodeURIComponent(obj[i]);
                } else if (_.isObject(obj[i]) || _.isArray(obj[i])) {
                    decodeObj(obj[i]);
                }
            }
        }
    }

    function recv(obj) {
        msgQueue.push(obj);
        update();
    }
    
    function selfRecv(obj) {
        msgQueue.push(obj);
    }

    function addListener(code, cb) {
        listenerMap[code] = cb;
    }

    function removeListener(code) {
        delete listenerMap[code];
    }

    function removeListeners(arr) {
        for (var i = 0; i < arr.length; i++)
            delete listenerMap[arr[i]];
    }

    function send(code, data) {
        var obj = {};
        obj.code = code;
        obj.data = data || {};
        if (gameData.uid)
            obj.uid = gameData.uid;

        var msgStr = JSON.stringify(obj);
        var sum = 0;
        for (var i = 0; i < msgStr.length; i++) {
            if (msgStr.charCodeAt(i) < 256)
                sum += msgStr.charCodeAt(i);
        }
        var sign = (sum + 245619426) % 15619472;

        var message = new Paho.MQTT.Message(sign + msgStr);
        message.destinationName = "n";
        client.send(message);

        cc.log("send: " + JSON.stringify(obj));
    }

    var connect = function (clientId, successCB, failureCB, ip) {
        if (isConnected)
            return;

        var url = gameData.url;
        var port = {200: 20000}[gameData.appId];

        // ip = '10.10.0.86';
        // port = 13200;

        // ip = '10.10.20.172';
        // port = 12102;

        // ip = '101.37.14.44';
        // port = 20000;

        url = url || "ws://" + ip + ":" + port + "/mqtt";
        // console.log("url = " + url);
        

        client = new Paho.MQTT.Client(url, clientId);


        //client._setTrace(function (d) {
        //    if (typeof d == 'object')
        //        console.log(JSON.stringify(d));
        //    else
        //        console.log(d);
        //});

        client.onConnectionLost = onConnectionLost;
        client.onMessageArrived = onMessageArrived;

        client.connect({
            timeout: 4,
            mqttVersion: 3,
            keepAliveInterval: 8,
            onSuccess: function () {
                onConnectSuccess();
                if (successCB)
                    successCB();
            }
            , onFailure: function () {
                onConnectFailure();
                if (failureCB)
                    failureCB();
            }
        });
    };

    var getLastPingInterval = function () {
        if (isConnected)
            return client.getLastPingInterval();
        else
            return 16888;
    };

    var disconnect = function () {
        client.disconnect();
    };

    var setOnDisconnectListener = function (cb) {
        disconnectCb = cb;
    };


    //消息
    var wsData = function (msg) {
        network.sendPhz(5000, msg);
    };
    function sendPhz(code, dt) {
        var obj = {};
        obj.code = code;
        obj.dt = dt;
        obj.data = {room_id: gameData.roomId};
        if (gameData.uid)
            obj.uid = gameData.uid;

        var msgStr = JSON.stringify(obj);
        var sum = 0;
        for (var i = 0; i < msgStr.length; i++) {
            if (msgStr.charCodeAt(i) < 256)
                sum += msgStr.charCodeAt(i);
        }
        var sign = (sum + 245619426) % 15619472;
        // sign = '';

        var message = new Paho.MQTT.Message(sign + msgStr);
        message.destinationName = "n";
        client.send(message);

        cc.log("send: " + msgStr);
    }
    function getMsgQueueNum() {
        // for(var i=0;i<msgQueue.length;i++){
        //     console.log(msgQueue[i].code);
        // }
        return msgQueue.length;
    }
    return {
        connect: connect
        , disconnect: disconnect
        , setOnDisconnectListener: setOnDisconnectListener
        , addListener: addListener
        , removeListener: removeListener
        , removeListeners: removeListeners
        , send: send
        , recv: recv
        , clearMsgs: clearMsgs
        , stop: stop
        , start: start
        , selfRecv: selfRecv
        , getNextMsgCode: getNextMsgCode
        , getLastPingInterval: getLastPingInterval
        , onConnectSuccess: onConnectSuccess
        , wsData: wsData
        , sendPhz: sendPhz
        , getMsgQueueNum: getMsgQueueNum
    }

})();
